﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufItem.h"
#include "GnufInfo.h"
#include "GnufButtonControl.h"
#include "GnufObjectPtr.h"

/// \file GnufButtonEx.h
/// \brief Gnuf中Button 的接口类

namespace gnuf {
    class ButtonAppearanceSetting;
    class QtItem;
    class QtButtonEx;

    /*
        / Inheritance Hierarchy /

        [ ButtonEx ] <---+--- [ PushButton ]
                         |
                         +--- [ CheckButton ]
                         |
                         +--- [ ToolButton ] <---+--- [ DropDownButton ]
                                                 |
                                                 +--- [ ComboBoxButton ]
    */

    /// \brief Gnuf中按钮的接口类
    /// \details 不能直接创建ButtonEx的对象，只能通过make 创建细分类型
    /// \see ItemWith QtButtonEx ButtonControlAdpatation make
    class GNUF_BASE_EXPORT ButtonEx
        : public ItemWith<QtButtonEx>
        , public ButtonControlAdpatation
    {
        GNUF_WATCHABLE_OBJECT(ButtonEx)
    public:
        static const EItemType Type;
        /// \brief 创建一个按钮
        /// \param type 按钮的类型
        /// \param parent 按钮的父控件
        /// \return 返回一个ButtonEx的智能指针
        static gcmp::OwnerPtr<ButtonEx> make(EItemType type, Item * parent);
        typedef std::function<void(ButtonAppearanceSetting*)> CustomizeRefreshFunction;
    public:
        virtual ~ButtonEx() { disconnectFromQtItem(); }

    public:
        /// \brief 设置按钮的Info
        /// \param info 要设置的Info
        void assignInfo(const Info& info);
        void assignInfo(const Info& info, const std::wstring& pixmapStream);

        /// \brief 设置toggle按钮的第二个Info
        /// \details 所谓Toggle，就是两个相关按钮放在同一个位置，点击一个按钮，会执行命令并切换到另一个按钮
        /// \param info2 第二个按钮Info
        void assignInfo2(const Info& info2);
        const Info * info2() const { return m_twinsInfo.second.get(); }
        const std::wstring& pixmapStream() const { return m_pixmapStream; }

        /// \brief 为按钮配置单独的AppearanceSetting，以便在同一个场景下使用不同样式的按钮。
        /// \param opSetting 需要设置的AppearanceSetting，切记Button对AppearanceSetting拥有所有权。
        void assignCustomizeAppearanceSetting(gcmp::OwnerPtr<ButtonAppearanceSetting>&& opSetting);

        /// \brief 为按钮配置自定义的刷新函数，以便在深浅主题切换时，正确地刷新自定义AppearanceSetting。
        /// \param fun 函数对象，接受一个ButtonAppearanceSetting*的参数。
        void assignCustomizeRefresher(const CustomizeRefreshFunction& fun) { m_customRefresher = fun; };
        const ButtonAppearanceSetting* customizeAppearanceSetting() const { return m_customizeAppearanceSetting.get(); }
        inline bool hasCustomizeAppearanceSetting() const { return m_customizeAppearanceSetting.get() != nullptr; }

        void setSelected(bool val) { onSelectionChanged(val); }
        bool isSelected() const { return m_bSelected; }

        virtual void click();

        void showIcon(bool val);
        bool isIconVisible() const { return m_bShowIcon; }

        void showText(bool val);
        bool isTextVisible() const { return m_bShowText; }

        virtual void setMaxCaptionCount(int val) override;
        int maxCaptionCount() const { return m_maxCaptionCount; }

        void showTooltip(bool val);
        bool isTooltipVisible() const { return m_bShowTooltip; }

        void setIsRecordUI(bool val) { m_bRecordUI = val; }
        bool isRecordUI() const { return m_bRecordUI; }

        /// \brief 设置按钮的展示样式
        /// \param style 按钮的样式，主要设置是否显示文字 和图标
        /// \see EButtonDisplayStyle
        void setButtonDisplayStyle(EButtonDisplayStyle style);

        // Just switch between the info and the info2. That is, present
        // the info if not toggled (by default), or present the info2 
        // if toggled. See also, assignInfo() and assignInfo2() methods.
        void toggle();
        inline bool canToggle() const;
        bool isToggled() const { return m_bToggled; }

        // A badge might be a mark or a text. By default, it is a mark.
        const std::wstring & badgeText() const { return m_badgeText; }

        ///\brief Set the sprite.
        void setSprite(const Sprite &);
        ///\brief Clear the sprite.
        void clearSprite();
        ///\return Return the sprite.
        const Sprite * sprite() const { return m_opSprite.get(); }

    public:
        virtual void onClicked() override;
        virtual void setBadgeText(const std::wstring&) override;

        // 错误信息接口
        virtual void onSpriteChanged(const Sprite &) override;

    protected:
        virtual QtItem* getQtItem() const override;
        // Inherits from Item...
        virtual void onDisconnectFromMyQtItem() override;
        virtual void onPostAbilityChanged() override;
        virtual void onPostInfoChanged() override;
        virtual void onPostVisibilityChanged() override;
        virtual void onPostAppearanceSettingChanged() override;
        virtual void onTransferredDueTo(const Item* pSource) override;
        virtual bool onIsA(const EItemType type) const override
        {
            // Since ButtonEx class is the basic class of some buttons, the value
            // that Item::type() returns does not equal ButtonEx::Type. Therefore, 
            // override Item::onIsA() virtual method in order to cast to ButtonEx.
            // See aslo Item::cast() template method. -frank
            return (type == ButtonEx::Type);
        }
        virtual void onPostBadgeVisibilityChanged() override;
        virtual void onTableRowSelectionChanged(bool) override;

        // Inherits from ButtonControlAdaptation...
        virtual void onVisibilityChanged(bool val) override { setVisible(val); }
        virtual void onAbilityChanged(bool val) override { setEnabled(val); }
        virtual void onSelectionChanged(bool) override;
        virtual void onCollectLocation(std::set<const gcmp::UniqueString *> &) const override;
        virtual void onConnectedWith(InputControl *) override;
        virtual void onInfoChanged(const std::pair< const Info *, const Info * > &) override;
        virtual void setCustomizeAppearanceSetting(gcmp::OwnerPtr<ButtonAppearanceSetting>&) override;
        virtual void showBadge(bool) override;
        virtual void onToggled(bool) override;
        virtual void onGetLocation(std::set<const gcmp::UniqueString*>*) const override;
    protected:
        explicit ButtonEx(Item *, EItemType, const char *);

    private:
        // the macro CANNOT_COPY_OR_ASSIGN in gcmp is not good
        // because it has not function definition
        // this can lead to the failure to compile in GCC
        // use the keyword 'delete' of cpp11 instead of CANNOT_COPY_OR_ASSIGN
        ButtonEx(const ButtonEx&) = delete; 
        ButtonEx& operator=(const ButtonEx&) = delete;

    private:
        std::pair< 
            gcmp::OwnerPtr<Info>, // by default
            gcmp::OwnerPtr<Info>  // for toggling
        > m_twinsInfo;
        gcmp::OwnerPtr<ButtonAppearanceSetting> m_customizeAppearanceSetting;
        bool m_bSelected;
        bool m_bShowIcon;
        bool m_bShowText;
        bool m_bShowTooltip;
        bool m_bRecordUI;
        std::wstring m_pixmapStream;
        std::wstring m_badgeText;
        bool m_bToggled;
        CustomizeRefreshFunction m_customRefresher;
        int m_maxCaptionCount;

        gcmp::OwnerPtr< Sprite > m_opSprite;
    };
}
